<?php

namespace Yxy\YQrcode;


use Endroid\QrCode\Color\Color;
use Endroid\QrCode\Encoding\Encoding;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\QrCode as EQCode;
use Endroid\QrCode\Label\Label;
use Endroid\QrCode\Logo\Logo;
use Endroid\QrCode\RoundBlockSizeMode;
use Endroid\QrCode\Writer\PngWriter;
use Exception;
use Random\RandomException;

/**
 * 二维码工具
 */
class Qrcode
{
    /**
     * @param array|string $content 内容
     * @param int $type 输出方式   1  保存本地并返回路径   2  直接输出图片   3  输出base64的图片  默认直接输出图片
     * @param int $size 大小  默认200
     * @return array|string|null
     *
     * @throws Exception
     * @example
     * <code>
     *     $content = [
     *          "content" => "内容",
     *          "level" => "等级  默认 high  1 Low  2 Medium 3 Quartile",
     *          "margin" => "边距  默认 3",
     *          "f_color" => "前景色  默认黑色 [0,0,0]",
     *          "b_color" => "背景色  默认白色 [255,255,255]",
     *          "logo" => "二维码logo  可只传字符串path或数组   ['路径','大小'] 或 ['path' => '路径','size' => 大小(数字)]",
     *          "label" => "logo下方文字  可传字符串或数组  ['路径','颜色数组[0,0,0]'] 或  ['text' => '文字','color' => 颜色数组[0,0,0]]"
     *      ];
     *      Qrcode::create($content, 2, 200)
     *
     *      Qrcode::create("你好")
     *
     *      Qrcode::create([
     *          'content' => "https://www.baidu.com",
     *          'f_color' => [10, 10, 10],
     *          'logo' => 'D:/phpstudy_pro/WWW/backend-laravel-version/public/static/images/default_user.png',
     *      ])
     * </code>
     */
    public static function create(array|string $content, int $type = 2, int $size = 200): array|string|null
    {
        $writer = new PngWriter();

        [$code_content, $level, $margin, $foreground_color, $background_color] = self::handelContent($content);

        $qrCode = new EQCode(
            data: $code_content,
            encoding: new Encoding('UTF-8'),
            errorCorrectionLevel: $level,
            size: $size,
            margin: $margin,
            roundBlockSizeMode: RoundBlockSizeMode::Margin,
            foregroundColor: $foreground_color,
            backgroundColor: $background_color
        );

        // logo
        $logo = self::handleLogo($content, $size);
        // 下方文字
        $label = self::handleLabel($content);
        // 生成二维码数据对象(后面两个参数都是可选的)
        $result = $writer->write($qrCode, $logo, $label);

        return self::outPut($result, $type);
    }

    /**
     * @param $content
     * @return array
     */
    protected static function handelContent($content): array
    {
        $code_content = 'QrCode';
        $level = ErrorCorrectionLevel::High;
        $margin = 3;
        $foreground_color = new Color(0, 0, 0);
        $background_color = new Color(255, 255, 255);

        if (is_string($content)) {
            $code_content = $content;
        } elseif (is_array($content)) {
            if (!empty($content['content'])) {
                $code_content = $content['content'];
            }
        }

        if (is_array($content)) {
            if (!empty($content['level'])) {
                switch ($content['level']) {
                    case 1:
                        $level = ErrorCorrectionLevel::Low;
                        break;
                    case 2:
                        $level = ErrorCorrectionLevel::Medium;
                        break;
                    case 3:
                        $level = ErrorCorrectionLevel::Quartile;
                        break;
                }
            }

            if (!empty($content['margin'])) {
                $margin = intval($content['margin']);
            }

            if (!empty($content['f_color']) && is_array($content['f_color']) && count($content['f_color']) >= 3) {
                [$red, $green, $blue] = self::handelColor($content['f_color']);
                $foreground_color = new Color($red, $green, $blue);
            }


            if (!empty($content['b_color']) && is_array($content['b_color']) && count($content['b_color']) >= 3) {
                [$red, $green, $blue] = self::handelColor($content['b_color']);
                $background_color = new Color($red, $green, $blue);
            }
        }

        return [$code_content, $level, $margin, $foreground_color, $background_color];
    }

    /**
     * @param $content
     * @param $size
     * @return Logo|null
     */
    protected static function handleLogo($content, $size): ?Logo
    {
        $logo = null;
        if (is_array($content) && !empty($content['logo'])) {
            $qrcode_logo = $content['logo'];
            $logo_path = null;
            $logo_size = null;
            // 二维码logo logo
            if (is_array($qrcode_logo)) {
                if (!empty($qrcode_logo[0])) {
                    $logo_path = $qrcode_logo[0];
                } elseif (!empty($qrcode_logo['path'])) {
                    $logo_path = $qrcode_logo['path'];
                }
                if (!empty($qrcode_logo[1])) {
                    $logo_path = $qrcode_logo[1];
                } elseif (!empty($qrcode_logo['size'])) {
                    $logo_path = $qrcode_logo['size'];
                }
            } else {
                $logo_path = $qrcode_logo;
                $logo_size = $size / 5;
            }
            if (!empty($logo_path) && !empty($logo_size)) {
                $logo = new Logo(path: $logo_path, resizeToWidth: $logo_size);
            }

        }
        return $logo;
    }

    /**
     * @param $content
     * @param int $default_color
     * @return Label|null
     */
    protected static function handleLabel($content, int $default_color = 90): ?Label
    {
        $label = null;
        if (is_array($content) && !empty($content['label'])) {
            $label_content = $content['label'];
            $text = null;
            $color = null;
            if (is_array($label_content)) {
                if (!empty($label_content['text'])) {
                    $text = $label_content['text'];
                }
                if (!empty($label_content['color']) && is_array($label_content['color']) && count($label_content['color']) >= 3) {
                    $color_list = $label_content['color'];
                    [$red, $green, $blue] = self::handelColor($color_list, $default_color);
                    $color = new Color($red, $green, $blue);
                }
            } elseif (is_string($label_content)) {
                $text = $label_content;
                $color = new Color($default_color, $default_color, $default_color);
            }
            // 二维码下方文字 label
            if (!empty($color) && !empty($text)) {
                $label = new Label(text: $text, textColor: $color);
            }

        }
        return $label;
    }

    /**
     * @param $color_list
     * @param int $default_color
     * @return array|int[]
     */
    protected static function handelColor($color_list, int $default_color = 90): array
    {
        [$red, $green, $blue] = array_values($color_list);
        $red = intval($red);
        $green = intval($green);
        $blue = intval($blue);
        if ($red < 0 || $red > 255) {
            $red = $default_color;
        }
        if ($green < 0 || $green > 255) {
            $green = $default_color;
        }
        if ($blue < 0 || $blue > 255) {
            $blue = $default_color;
        }
        return [$red, $green, $blue];
    }

    /**
     * 获取域名
     * @return string
     */
    protected static function getDomain(): string
    {
        $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http';
        $host = $_SERVER['HTTP_HOST'];

        return $protocol . '://' . $host;
    }

    /**
     * 获取查看地址
     * @param string $url
     * @return string
     */
    protected static function getHttpPath(string $value): string
    {
        if (empty($value)) {
            return '';
        }
        $preg = "/^http(s)?:\\/\\/.+/";
        if (preg_match($preg, $value)) {
            return $value;
        }
        return self::getDomain() . $value;
    }

    /**
     * 生成随机字符串
     * @param $length
     * @return string
     * @throws RandomException
     */
    protected static function createUniqueCode($length): string
    {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $charactersLength = strlen($characters);
        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[random_int(0, $charactersLength - 1)];
        }
        return $randomString;
    }

    /**
     * 返回项目根目录
     * @return string
     */
    protected static function getRootDir(): string
    {
        if (function_exists('public_path')) {
            return public_path();
        }
        return $_SERVER['DOCUMENT_ROOT']; // 返回项目根目录
    }

    /**
     * 创建图片文件
     * @param string $path 要创建的路径
     * @param string $file_name 文件名称
     * @return string[]
     * @throws Exception
     */
    protected static function makeImage(string $path, string $file_name = ''): array
    {
        $name = empty($file_name) ? self::createUniqueCode(16) : $file_name;

        $public = self::getRootDir();

        $dir = '/y-qrcode/' . $path;

        $filename = '/' . $name . '.png';

        $mkdir = $public . $dir;

        if (!is_dir($mkdir)) {
            mkdir($mkdir, 0755, true);
        }

        return [$public . $dir . $filename, $dir . $filename];
    }

    /**
     * @param $result
     * @param $type
     * @return array|string|void
     * @throws Exception
     */
    protected static function outPut($result, $type)
    {
        if ($type == 2) {
            // 直接输出在浏览器中
            if (ob_get_level() > 0) {
                ob_end_clean(); //处理在TP框架中显示乱码问题
            }
            header('Content-Type: ' . $result->getMimeType());
            echo $result->getString();
        } elseif ($type == 3) {
            // 返回 base64 格式的图片
            $dataUri = $result->getDataUri();
            return "<img src='{$dataUri}' alt='二维码'>";
        } else {
            // 将二维码图片保存到本地服务器
            [$uploadPath, $filePath] = self::makeImage('qrcode');
            $result->saveToFile($uploadPath);
            return [
                'url' => $filePath,
                'show' => self::getHttpPath($filePath)
            ];
        }
    }
}
